﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using MetaCollection.Framework.Change;
using MetaCollection.Framework.Model;
using System.Globalization;

namespace MetaCollection.Framework.Repository
{
	internal class DistributedItemRepository : DistributedRepository<IItemRepository>, IItemRepository
	{

		#region IItemRepository Members

		/// <summary>
		/// Gets all versions of an item
		/// </summary>
		/// <param name="id">The id of the item.</param>
		/// <param name="opt">Load Options for what data to return</param>
		/// <returns>
		/// An ItemInstance for the given Item Id or null if no object found
		/// </returns>
		public IEnumerable<IItemInstance> GetItem(Guid id, LoadOptions opt)
		{
			string key = ReferenceKey(id, null);
			foreach (var repo in ResolveReference(key))
			{
				foreach (var item in repo.GetItem(id, opt))
				{
					MarkReference(key, repo);
					yield return item;
				}
			}
		}

		/// <summary>
		/// Finds an Item by Id-Version.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <param name="version">The version.</param>
		/// <param name="opt">Load Options for what data to return</param>
		/// <returns>
		/// An ItemInstance for the given Item Id-Version or null if no object found
		/// </returns>
		public IItemInstance GetItem(Guid id, int version, LoadOptions opt)
		{
			string key = ReferenceKey(id, null);
			return ResolveReference(key).Select((i) => i.GetItem(id, version, opt)).SingleOrDefault();
		}

		/// <summary>
		/// Puts the item into persistant storage or updates an existing item.
		/// </summary>
		/// <param name="item"></param>
		/// <param name="changeCollection"></param>
		/// <param name="requestType"></param>
		/// <returns></returns>
		public IItemInstance SaveItem(IItemInstance item, ItemChangeCollection changeCollection) 
		{
			if (item == null)
				throw new ArgumentNullException("item", "Item can not be null.");

			IItemRepository repo;
			if (IsKnownReference(ReferenceKey(item.Id, null)))
				repo = ResolveReference(ReferenceKey(item.Id, null)).First();
			else if (IsKnownReference(ReferenceKey(item.ParentId, null)))
				repo = ResolveReference(ReferenceKey(item.ParentId, null)).First();
			else if (GetItem(item.Id, LoadOptions.ItemReference).FirstOrDefault() != null) // if repo reference is not found then attempt some gets to find the proper storage location.
				repo = ResolveReference(ReferenceKey(item.Id, null)).First();
			else if (GetItem(item.ParentId, LoadOptions.ItemReference).FirstOrDefault() != null) // if the repo still not found then search for the parent
				repo = ResolveReference(ReferenceKey(item.ParentId, null)).First();
			else
				repo = DefaultRepository;
			return repo.SaveItem(item, changeCollection); //, requestType);
		}

		/// <summary>
		/// Deletes the item.
		/// </summary>
		/// <param name="id">The id.</param>
		public void RemoveItem(Guid id)
		{
			string key = ReferenceKey(id, null);
			foreach (var repo in ResolveReference(key))
			{
				repo.RemoveItem(id);
			}
		}

		/// <summary>
		/// Deletes the item.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <param name="version">The version.</param>
		public void RemoveItem(Guid id, int version)
		{
			string key = ReferenceKey(id, null);
			foreach (var repo in ResolveReference(key))
			{
				repo.RemoveItem(id, version);
			}
		}

		/// <summary>
		/// Finds an item by name with a given parent.
		/// </summary>
		/// <param name="parentId">The parent id.</param>
		/// <param name="name">The name.</param>
		/// <param name="opt">Load Options for what data to return</param>
		/// <returns>an ItemInstance</returns>
		/// <remarks>Returns the published version ?? max(version) of each item</remarks>
		public IEnumerable<IItemInstance> FindByName(Guid parentId, string name, LoadOptions opt)
		{
			string key = ReferenceKey(parentId, name);
			foreach (var repo in ResolveReference(key))
			{
				foreach (var item in repo.FindByName(parentId, name, opt))
				{
					MarkReference(key, repo);
					yield return item;
				}
			}

		}

		/// <summary>
		/// Finds all the items with a given parent.
		/// </summary>
		/// <param name="parentId">The parent id.</param>
		/// <param name="includeIndirect">if set to <c>true</c> the children that are not directly under the parent are also included.</param>
		/// <param name="opt">Load Options for what data to return</param>
		/// <returns></returns>
		/// <remarks>Returns the published version ?? max(version) of each item</remarks>
		public IEnumerable<IItemInstance> FindByParent(Guid parentId, bool includeIndirect, LoadOptions opt)
		{
			string key = ReferenceKey(parentId, ".");
			foreach (var repo in ResolveReference(key))
			{
				foreach (var item in repo.FindByParent(parentId, includeIndirect, opt))
				{
					MarkReference(key, repo);
					yield return item;
				}
			}

		}

		#endregion
	}
}
